package top.goldenyear.porpoise.framework.web.core.generator.config;

import cn.hutool.v7.core.collection.CollUtil;
import cn.hutool.v7.core.regex.ReUtil;
import cn.hutool.v7.core.text.StrUtil;
import java.util.*;
import lombok.Data;
import lombok.experimental.Accessors;

/**
 * 策略配置
 *
 * @author 王帅
 * @since 2023-05-14
 */
@Data
@Accessors(chain = true)
@SuppressWarnings("unused")
public class StrategyConfig {
  private static DomainConfig commonDomainConfig = DomainConfig.of().setDomain("common");

  /** 是否生成视图映射 -- GETTER -- 是否生成视图 */
  private boolean generateForView;

  private Map<String, DomainConfig> domainConfigMap = new HashMap<>();

  /** 单独为某张表添加独立的配置 -- GETTER -- 获取表配置 */
  private Map<String, TableConfig> tableConfigMap = new HashMap<>();

  /** 设置某个列的全局配置 -- GETTER -- 获取列配置 */
  private Map<String, ColumnConfig> columnConfigMap = new HashMap<>();

  /**
   * 需要生成的表在哪个模式下 -- GETTER -- 设置要生成的模式
   *
   * @return
   */
  private String generateSchema;

  /** 只生成指定的表，如果未设置时，未指定的表也会生成，统一放在 common 包下面 */
  private boolean onlyGenAssignTable = false;

  /** 生成哪些表，白名单 -- GETTER -- 获取生成哪些表，支持完整的表名，或者正则表达式 */
  private Set<String> whitelist = new HashSet<>();

  /** 不生成哪些表，黑名单 -- GETTER -- 获取不生成哪些表，支持完整的表名，或者正则表达式 */
  private Set<String> blacklist = new HashSet<>();

  public static StrategyConfig of() {
    return new StrategyConfig();
  }

  public StrategyConfig addTableConfig(TableConfig tableConfig) {
    if (tableConfigMap == null) {
      tableConfigMap = new HashMap<>(1);
    }

    tableConfigMap.put(tableConfig.getTableName(), tableConfig);

    // 只生成指定的表，把表放入白名单中
    if (onlyGenAssignTable) {
      whitelist.add(tableConfig.getTableName());
    }

    return this;
  }

  public TableConfig getTableConfig(String tableName) {
    TableConfig tableConfig = tableConfigMap == null ? null : tableConfigMap.get(tableName);
    if (tableConfig == null) {
      tableConfig = TableConfig.of().setTableName(tableName);
    }

    return tableConfig;
  }

  public StrategyConfig addColumnConfig(ColumnConfig columnConfig) {
    if (columnConfigMap == null) {
      columnConfigMap = new HashMap<>(1);
    }

    columnConfigMap.put(
        String.format("%s.%s", columnConfig.getTableName(), columnConfig.getColumnName()),
        columnConfig);
    return this;
  }

  public ColumnConfig getColumnConfig(String tableName, String columnName) {
    ColumnConfig columnConfig =
        columnConfigMap == null
            ? null
            : columnConfigMap.get(String.format("%s.%s", tableName, columnName));
    if (columnConfig == null) {
      columnConfig = ColumnConfig.of().setTableName(tableName).setColumnName(columnName);
    }

    return columnConfig;
  }

  /**
   * 基于domain进行分组，未设置则不分组
   *
   * @param domainConfig
   * @return
   */
  public StrategyConfig addDomainConfig(DomainConfig domainConfig) {
    if (domainConfigMap == null) {
      domainConfigMap = new HashMap<>(1);
    }

    for (String table : domainConfig.getTables()) {
      table = StrUtil.trim(table);
      if (StrUtil.isBlank(table)) {
        continue;
      }

      domainConfigMap.put(table, domainConfig);
      if (onlyGenAssignTable) {
        Map<String, TableConfig> tableConfigs = domainConfig.getTableConfigMap();
        if (CollUtil.isNotEmpty(tableConfigs)) {
          tableConfigs.forEach((k, tableConfig) -> addTableConfig(tableConfig));
        }
      }
    }

    return this;
  }

  public Collection<DomainConfig> getDomainConfigs() {
    return domainConfigMap.values();
  }

  public DomainConfig getDomainConfig(String tableName) {
    if (domainConfigMap == null) {
      commonDomainConfig.addTable(tableName);
      return commonDomainConfig;
    }

    DomainConfig domainConfig = domainConfigMap.get(tableName);
    if (domainConfig == null) {
      commonDomainConfig.addTable(tableName);
      return commonDomainConfig;
    }

    return domainConfig;
  }

  /** 设置只生成哪些表 */
  public StrategyConfig setWhileList(String... tables) {
    for (String table : tables) {
      table = StrUtil.trim(table);
      if (StrUtil.isBlank(table)) {
        continue;
      }

      whitelist.add(table.trim());
    }

    return this;
  }

  /** 设置不生成哪些表 */
  public StrategyConfig setBlackList(String... tables) {
    for (String table : tables) {
      table = StrUtil.trim(table);
      if (StrUtil.isBlank(table)) {
        continue;
      }

      blacklist.add(table.trim());
    }

    return this;
  }

  public boolean isSupportGenerate(String table) {
    if (matchUnGenerateTable(table)) {
      return false;
    }

    return matchGenerateTable(table);
  }

  public boolean matchGenerateTable(String tableName) {
    // 不配置指定比表名的情况下，支持所有表
    if (CollUtil.isEmpty(this.getWhitelist())) {
      return true;
    }

    return this.matchTable(tableName, this.getWhitelist());
  }

  public boolean matchUnGenerateTable(String tableName) {
    if (CollUtil.isEmpty(this.getBlacklist())) {
      return false;
    }

    return this.matchTable(tableName, this.getBlacklist());
  }

  private boolean matchTable(String tableName, Set<String> matchTables) {
    return matchTables.stream().anyMatch((t) -> this.tableNameMatches(tableName, t));
  }

  private boolean tableNameMatches(String tableName, String tablePattern) {
    return tablePattern.equalsIgnoreCase(tableName) || ReUtil.isMatch(tablePattern, tableName);
  }
}
